#include <user/ulib.h>

uint32_t service_callback;
struct file_t *image_file;
int initial_idx;

int
user_init(void)
{
     int err;
     
	 /* initial the global variable */
	 image_file = NULL;
	 
	 /* Set the user page fault handler */
	 /* This will be set if needed */
	 /* set_pfhandler(&pfhandler_entry); */
	 
	 if ((err = user_page_init()) != 0) return err;
     if ((err = user_heap_init()) != 0) return err;
	 if ((err = rpc_init()) != 0) return err;
	 if ((err = file_init()) != 0) return err;

	 // sfork_test();
	 
	 initial_idx = sc_cur_env();

	 fork();
	 if (self_idx == initial_idx) {
		  /* initial process can be treat as a part of user process
		   * manager */
		  
		  struct env_t *env = envs + initial_idx;
		  int node;
		  
		  /* The parent process is used to recive spawn request */
		  while (1) {
			   waitb(ENV_SCH_IEC_RECV_BLOCKING);
			   uint16_t src = env->iec.recv.begin;
			   /* only receive request from up manager */
			   if (src != get_user_service(SVC_UP_MANAGER))
			   {
					kprintf("[init %d] receive request from %d, but %d expected\n",
							initial_idx,
							src, get_user_service(SVC_UP_MANAGER));
					iec_recv(src);
					continue;
			   }
			   node = envs[src].iec.cache.arg_a;
			   kprintf("[init %d] receive request %08x\n", initial_idx, node);
			   iec_recv(src);

			   fork();
			   if (self_idx != initial_idx) break;
		  }

		  /* Here to process the request */
		  struct file_t *file = file_alloc();
		  file->type = FD_NODE;
		  file->node = node;

		  uintptr_t entry;
		  elf_load(file, &entry);

		  kprintf("entry = %08x\n", entry);
		  asm volatile ("call *%0;" : : "m"(entry));
		  kprintf("returned\n");
		  while (1) ;
		  
	 }
	 else
	 {
		  /* Use the child process to start service */
		  /* 建立用户服务器 */
		  uint16_t main_idx = self_idx;
		  uint32_t r;
		  uint16_t f;

		  struct pipe_t *pipe = pipe_new();
		  service_callback = pipe_share(pipe);
		  
		  /* fork(); */
		  /* if (self_idx != main_idx) */
		  /* { */
		  /* 	   klogger_entry(); */
		  /* } */
		  /* else */
		  /* { */
		  /* 	   pipe = pipe_open(service_callback); */
		  /* 	   pipe_block_read(pipe, &r, sizeof(r)); */
		  /* 	   if (r) */
		  /* 			kprintf("Error while starting kernel logger service.\n"); */
		  /* 	   else kprintf("kernel logger service started.\n"); */
		  /* 	   pipe_close(pipe); */
		  /* } */

		  kprintf("Starting system services\n");
		  
#if 0
		  fork();
		  if (self_idx != main_idx)
		  {
			   file_server_entry();
		  }
		  else
		  {
			   pipe = pipe_open(service_callback);
			   pipe_block_read(pipe, &r, sizeof(r));
			   if (r)
					kprintf("Error while starting file server service.\n");
			   else kprintf("File server service started.\n");
			   pipe_close(pipe);
		  }

		  fork();
		  if (self_idx != main_idx)
		  {
			   up_manager_entry();
		  }
		  else
		  {
			   pipe = pipe_open(service_callback);
			   pipe_block_read(pipe, &r, sizeof(r));
			   if (r)
					kprintf("Error while starting user process manager service.\n");
			   else kprintf("User process manager service started.\n");
			   pipe_close(pipe);
		  }
		  
		  fork();
		  if (self_idx != main_idx)
		  {
		  	   input_entry();
		  }
		  else
		  {
		  	   pipe = pipe_open(service_callback);
		  	   pipe_block_read(pipe, &r, sizeof(r));
		  	   if (r)
		  			kprintf("Error while starting input service.\n");
		  	   else kprintf("input service started.\n");
		  	   pipe_close(pipe);
		  }

#endif

		  fork();
		  if (self_idx != main_idx)
		  {
		  	   pci_entry();
		  }
		  else
		  {
		  	   pipe = pipe_open(service_callback);
		  	   pipe_block_read(pipe, &r, sizeof(r));
		  	   if (r)
		  			kprintf("Error while starting pci service.\n");
		  	   else kprintf("pci service started.\n");
		  	   pipe_close(pipe);
		  }

		  if (self_idx != main_idx)
		  {
			   kprintf("library returned");
			   while (1) ;
		  }
		  else return 0;
	 }
}

int
pfhandler(struct trapframe_t tf, void *addr, uint32_t err, uint32_t level)
{
	 kprintf("[pfhandler] addr = %08x\n", addr);
	 return fix_addr(addr);
}

uint16_t self_idx;

uint16_t
fork(void)
{
	 uint16_t result = sc_fork();
	 /* if (result != 0) */
		  self_idx = sc_cur_env();
	 return result ^ self_idx;
}

void
sl_lock(uint8_t *sl)
{
	 while (!sl_trylock(sl))
		  wait();
}

int
spawn(char *filename)
{
	 int len = strlen(filename);
	 kprintf("spawn %s ", filename);
	 
	 pipe_clear(rpc_in_pipe, 0);
	 pipe_clear(rpc_out_pipe, 0);

	 kprintf("up_manager = %d\n", get_user_service(SVC_UP_MANAGER));
	 rpc_send(get_user_service(SVC_UP_MANAGER), UPC_SPAWN);
	 pipe_block_write(rpc_in_pipe, &len, sizeof(uint32_t));
	 pipe_block_write(rpc_in_pipe, filename, len);

	 rpc_wait();

	 return 0;
}
